

/**
 * @description: 严格类型判断
 * @param {any} typeObject
 * @return {String} Object Array
 */
function $_toClass(typeObject) {
  return Object.prototype.toString.call(typeObject).split(" ")[1].replace("]", "");
}

/**
 * @description: 严格类型判断
 * @param {any} typeObject
 * @param {String} type
 * @return {Boolean}
 */
function $_isType(typeObject, type) {
  let targetType = $_toClass(typeObject);
  return targetType === type;
}

/**
 * @description: 查找key是否在对象上（不包括对象原型）
 * @param {Object} obj
 * @param {String} key
 * @param {Boolean} typeVf 是否对obj参数进行强类型验证
 * @return {Boolean}
 */
function $_hasOwn(obj, key , typeVf = true) {
  if( obj ){
    if( (typeVf &&  $_isType(obj, "Object"))
        || !typeVf
      )
    {
      return Object.hasOwnProperty.call(obj, key);
    }
  }else{
    throw Error("the obj params is required");
  }
}

/**
 * @description: 字符串去除空格
 * @param {String} str  
 * @param {String} insetAuto 是否去除字符串内部空白符
 * @return {String}
 */
function $_trimAll(str, insetAuto) {
  if (str) {
    if (!insetAuto) {
      return str.trim();
    } else {
      return str.replace(/\s/gm, "");
    }
  } else {
    return str;
  }
}

/**
 * @description: 浅合并
 * @param {any}
 * @return {Object}
 */

const $_copy = Object.assign;

/**
 * @description: 深拷贝
 * @param {Object} jsonObject
 * @return {Object}
 */

function $_extend(jsonObject) {
  return JSON.parse(JSON.stringify(jsonObject));
}

/**
 * @description: 混入
 * @param {Object} to
 * @param {Object} from
 * @return {Object}
 */
function $_merge(to, from) {
  if (!from) return to;
  let key, toVal, fromVal;
  const keys = Object.keys(from);
  for (let i = 0, len = keys.length; i < len; i++) {
    key = keys[i];
    // in case the object is already observed...
    if (key === '__ob__') continue;
    toVal = to[key];  // child
    fromVal = from[key]; // parent
    if (!$_hasOwn(to, key)) {
      to[key] = fromVal;  //若to没有此key，添加它
    } else if (
      toVal !== fromVal &&
      $_isType(toVal, "Object") &&
      $_isType(fromVal, "Object")
    ) {
      //若to有此key，且不等
      //若to有此key，值非对象，否则进行深度合并
      $_merge(toVal, fromVal);
    }
  }
  return to;
}

/**
 * @description: 数组合并(去重) 依赖Set对象
 * @param {Array} originArr
 * @param {Array} mergeArr
 * @param {String} deepKey 对 Array item为Object类型时，进行深度去重。（ 默认情况下如果原数组与合并数组的对象item指向同一个内存，不会产生合并 ）
 * @return {Object} [ union//合集 , intersection//交集 ,diff// 差集 ]
 */

 function $_union(originArr , mergeArr , deepKey) {
   if( originArr && $_isType( originArr , "Array" ) ){
    const union = [];
    const intersection = [];
    const diff = [];
    if( mergeArr && $_isType( originArr , "Array" ) ){
      if( mergeArr.length ){
        const originArr_set = new Set( originArr );
        const mergeArr_set = new Set( mergeArr );
        //有指定去重key的情况  且  合并item为对象类型时
        let deepKeyHas = false;
        let originArr_ObjectKeys;
        if( deepKey && typeof deepKey === "string" ){
          deepKeyHas = true;
          originArr.forEach(item=>{
            if( item && $_isType( item , Object ) ){
              originArr_ObjectKeys.push( item[deepKey] );
            }
          });
        }
        mergeArr_set.forEach(mergeItem=>{
          if( originArr_set.has( mergeItem ) ){
            intersection.push( mergeItem );
          }else{
            if( deepKeyHas 
              && $_isType( mergeItem , "Object" )
            ){
              if( originArr_ObjectKeys.include( mergeItem[deepKey] ) ){
                intersection.push( mergeItem );
              }else{
                diff.push( mergeItem );
              }
            }else{
              diff.push( mergeItem );
            }
          }
        });
      }
      union.push( ...originArr , ...diff );
      return {
        union
        ,intersection
        ,diff
      }
    }else{
      throw Error("params mergeArr is required or type be Array");
    }
   }else{
    throw Error("params originArr is required or type be Array");
   }

 }



/**
 * @description: await-to  同步处理await运算符错误
 * @param {Promise} promise
 * @param {Object} errorExt
 * @return {Array} [ error , result ]
 * @git https://github.com/scopsy/await-to-js
 */
function $_to(promise, errorExt) {
  return promise
    .then((data) => [null, data])
    .catch((err) => {
      if (errorExt) {
        $_copy(err, errorExt);
      }
      return [err, undefined];
    });
}


/**
 * @description: 防抖工厂函数
 * @param {Function} fn
 * @param {Number} delay
 * @return {Function}
 */
function $_debounce(fn, delay) {
  let timeout = null; // 创建一个标记用来存放定时器的返回值
  return function () {
    clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
    timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话，就不会执行 fn 函数
      fn.apply(this, arguments);
    }, delay);
  };
}


/**
 * @description: 节流工厂函数
 * @param {Function} fn
 * @param {Number} delay
 * @return {Function}
 */
function $_throttle(fn, delay) {
  let canRun = true; // 通过闭包保存一个标记
  return function () {
    if (!canRun) return; // 在函数开头判断标记是否为true，不为true则return
    canRun = false; // 立即设置为false
    setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中
      fn.apply(this, arguments);
      // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false，在开头被return掉
      canRun = true;
    }, delay);
  };
}

module.exports = {
  $_copy
  , $_extend
  , $_merge
  , $_union
  , $_toClass
  , $_isType
  , $_hasOwn
  , $_trimAll
  , $_to
  , $_debounce
  , $_throttle
}